/***************************************************************************
 *   This is a part of the NextCAD Web APP.                                *
 *   Copyright (C) AMCAX, All rights reserved.                             *
 ***************************************************************************/

import { KEYBOARD_KEY } from '@app-cad/common/Constants';
import { wsApi } from '@app-cad/server';
import { Vector3 } from 'three-legacy';

import {
  CadScene,
  CENTEREVENTS,
  CSCircle,
  CSLine,
  css2dInputLable,
  MathTool,
  SceneGraph,
} from '@amcax/base';
import { eventCenterManager } from '@app-base/common/events/EventCenterManager';
import { ctx2 } from '@app-cad/module';

type MouseCylinderState = 'none' | 'draw' | 'extrude';

interface TrackingData {
  circle1: CSCircle;
  circle2: CSCircle;
  rline: CSLine;
  hline: CSLine;
  rlabel: css2dInputLable;
}

const cmdMouseCylinderInit = async (cmdContext) => {
  const cadScene = ctx2.cadScene as CadScene;
  const sceneSetup = ctx2.viewer.sceneSetup;
  const assisGroup = cadScene.assisGroup;
  eventCenterManager.activeEventCenter.setState('drawing');

  // 固定为 XOY 平面
  const planeNormal: Vector3 = new Vector3(0, 0, 1);
  const planeOrigin: Vector3 = new Vector3(0, 0, 0);

  const lineWidth = 1; // 绘制的线宽
  const lineColor = 0x1890f5;
  let stepStatus: MouseCylinderState = 'none';

  const previewLines: TrackingData = {
    circle1: null,
    circle2: null,
    rline: null,
    hline: null,
    rlabel: null,
  };

  let startPoint: Vector3; // 起点 = 圆心
  let endPoint: Vector3; // 终点 = 圆上的某点
  let radius = 0;
  let height = 0;
  let direct: number = 1; // 向上拉还是向下拉

  // 注册鼠标键盘
  document.addEventListener('keyup', keyupEvent);
  const canvas = ctx2.container;
  canvas.addEventListener('mouseup', onMouseClick);

  eventCenterManager.activeEventCenter.on(
    CENTEREVENTS.POINTERMOVE,
    onMouseMove,
  );

  // 命令完成或者取消，释放资源
  async function exitCmdMouseCylinder() {
    stepStatus = 'none';

    if (cmdContext) {
      cmdContext.store.highlightCmdId = '';
    }

    eventCenterManager.activeEventCenter
      .off(CENTEREVENTS.POINTERMOVE, onMouseMove)
      .setDefault();
    canvas.removeEventListener('mouseup', onMouseClick);
    document.removeEventListener('keyup', keyupEvent);

    previewLines.circle1 && previewLines.circle1.destroy();
    previewLines.circle2 && previewLines.circle2.destroy();
    previewLines.rline && previewLines.rline.destroy();
    previewLines.hline && previewLines.hline.destroy();
    previewLines.rlabel && previewLines.rlabel.destroy();
    assisGroup.clear();
    SceneGraph.emptyGroup(assisGroup);
    ctx2.viewer.requestRender();
  }

  // 命令完成，调用API
  async function drawDoneCallback() {
    let pos = [startPoint.x, startPoint.y, 0];
    if (direct < 0) {
      pos = [startPoint.x, startPoint.y, Math.abs(height) * -1];
    }
    await wsApi.createPrimitive(
      'Cylinder',
      {
        radius: radius,
        height: Math.abs(height),
      },
      [pos, [0, 0, 1], [1, 0, 0]],
    );
  }

  // 键盘事件：取消 / 参数输入框回车
  function keyupEvent(e) {
    if (e.key == KEYBOARD_KEY.ESC) {
      exitCmdMouseCylinder();
      return;
    }
    if (e.key == KEYBOARD_KEY.ENTER) {
      if (stepStatus == 'draw') {
        radius = Math.abs(previewLines.rlabel.getText());
        stepStatus = 'extrude';
      } else if (stepStatus == 'extrude') {
        height = previewLines.rlabel.getText();
        drawDoneCallback();
        exitCmdMouseCylinder();
      }
    }
  }

  // 鼠标点击，绘制步骤确定并进入下一个步骤
  function onMouseClick() {
    if (stepStatus == 'none') {
      stepStatus = 'draw';
      return;
    }
    if (stepStatus == 'draw') {
      stepStatus = 'extrude';
      return;
    }
    if (stepStatus == 'extrude') {
      drawDoneCallback();
      exitCmdMouseCylinder();
    }
  }

  // 鼠标移动，记录鼠标点并计算需要的值
  function onMouseMove(e) {
    if (stepStatus == 'none') {
      startPoint = sceneSetup.mouseOnPlane(
        e.event.layerX,
        e.event.layerY,
        planeNormal,
        planeOrigin,
      );
    }
    if (stepStatus == 'draw') {
      endPoint = sceneSetup.mouseOnPlane(
        e.event.layerX,
        e.event.layerY,
        planeNormal,
        planeOrigin,
      );
      radius = startPoint.clone().sub(endPoint).length();
      showPreview();
    }
    if (stepStatus == 'extrude') {
      const camNormal = sceneSetup.getCameraNormal().clone().normalize();
      const extrudePoint = sceneSetup.screenToWorld(
        e.event.layerX,
        e.event.layerY,
        camNormal.clone(),
        endPoint,
      );
      height = extrudePoint.z;
      direct = height < 0 ? -1 : 1;
      if (height != 0) {
        showPreview();
      }
    }
  }

  function showPreview() {
    // draw cirle
    if (stepStatus == 'draw') {
      if (!previewLines.circle1) {
        previewLines.circle1 = new CSCircle({
          center: startPoint,
          radius: radius,
          csys: MathTool.createCsysFromPlacement([
            [startPoint.x, startPoint.y, 0],
            [0, 0, 1],
            [1, 0, 0],
          ]),
          lineWidth,
          color: lineColor,
        });
        previewLines.circle1.addTo(assisGroup);
      } else {
        previewLines.circle1.update({ radius: radius });
      }

      // draw circle radius
      if (!previewLines.rline) {
        previewLines.rline = new CSLine({
          vertexs: [
            new Vector3(startPoint.x, startPoint.y, 0),
            new Vector3(endPoint.x, endPoint.y, 0),
          ],
          dashed: true,
          color: lineColor,
          lineWidth,
          depthTest: true,
        });
        previewLines.rline.addTo(assisGroup);
      } else {
        previewLines.rline.setVertexs([
          new Vector3(startPoint.x, startPoint.y, 0),
          new Vector3(endPoint.x, endPoint.y, 0),
        ]);
      }
    }
    // draw cylinder wireframe，画顶面的圆 + 一个竖线
    if (stepStatus == 'extrude') {
      if (!previewLines.circle2) {
        previewLines.circle2 = new CSCircle({
          name: 'mc-e2',
          center: new Vector3(startPoint.x, startPoint.y, height),
          radius: radius,
          csys: MathTool.createCsysFromPlacement([
            [startPoint.x, startPoint.y, height],
            [0, 0, 1],
            [1, 0, 0],
          ]),
          lineWidth,
          color: lineColor,
        });
        previewLines.circle2.addTo(assisGroup);
      } else {
        previewLines.circle2.update({
          center: new Vector3(startPoint.x, startPoint.y, height),
          radius: radius,
          csys: MathTool.createCsysFromPlacement([
            [startPoint.x, startPoint.y, height],
            [0, 0, 1],
            [1, 0, 0],
          ]),
        });
      }

      // draw cylinder height line
      if (!previewLines.hline) {
        previewLines.hline = new CSLine({
          vertexs: [
            new Vector3(endPoint.x, endPoint.y, 0),
            new Vector3(endPoint.x, endPoint.y, height),
          ],
          dashed: true,
          color: lineColor,
          lineWidth,
          depthTest: true,
        });
        previewLines.hline.addTo(assisGroup);
      } else {
        previewLines.hline.setVertexs([
          new Vector3(endPoint.x, endPoint.y, 0),
          new Vector3(endPoint.x, endPoint.y, height),
        ]);
      }
    }
    // draw input label
    const labelText = stepStatus == 'draw' ? radius : height;
    const labelPos =
      stepStatus == 'draw'
        ? new Vector3(endPoint.x, endPoint.y, 0)
        : new Vector3(endPoint.x, endPoint.y, height);
    if (!previewLines.rlabel) {
      previewLines.rlabel = new css2dInputLable({
        text: labelText,
        position: labelPos,
      });
      previewLines.rlabel.focus();
      previewLines.rlabel.addTo(assisGroup);
    } else {
      previewLines.rlabel.update(labelText, labelPos);
    }

    ctx2.viewer.requestRender();
  }
};

export const cmdMouseCylinder = {
  id: 'CmdMouseCylinder',
  label: 'command.cylinder',
  icon: 'CmdCylinder',
  invoke: cmdMouseCylinderInit,
};
